package com.daikun.common.utils;

import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpStatus;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.daikun.common.enums.BaseResultEnum;
import com.daikun.common.exception.BizException;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.List;

/**
 * 从核心得 id 生成器服务中获取一个全局唯一的 id
 * <a href="https://xunmotech.feishu.cn/wiki/HUYdwTs4hinu52kwmNGcVnKgn6g">id 生成器接口文档</a>
 * 当前类所需的 nacos 配置，不配置有默认值
 *  -> sys.param.idGen.singletonUrl     获取单个 id 的接口地址，不配置默认 泰国 的地址
 *  -> sys.param.idGen.batchUrl         获取批量 id 的接口地址，不配置默认 泰国 的地址
 *  -> sys.param.idGen.batchSize        单次获取批量 id 的最大个数，不配置默认最大 1000
 *  -> sys.param.idGen.timeout          请求获取 id 的接口超时时间，单位毫秒，默认 2000
 * @author earl created 2025-07-16
 * @version init 0.0.1
 */
@Slf4j
public class IdGenUtil {

    // 调用接口的超时时间，单位 毫秒
    private static final int timeout = Integer.parseInt(StrUtil.blankToDefault(SpringUtil.getProperty("sys.param.idGen.timeout"), "2000"));
    // 批量获取 id 接口地址
    private static final String batchUrl = "/api/v1/getBatchGenerateId";
    // 单个获取 id 接口地址
    private static final String singletonUrl = "/api/v1/getGenerateId";
    // sysId - 此处使用当前服务的名称作为 sysId，服务提供方会进行记录，方便后续分析
    private static final String sysId = StrUtil.blankToDefault(SpringUtil.getProperty("spring.application.name"), "idaas-system");
    // true 当前是测试环境，false 当前是生产环境
    private static final boolean isTestEnv = Boolean.parseBoolean(StrUtil.blankToDefault(SpringUtil.getProperty("sys.param.isDingdingTest"), "true"));

    /**
     * 获取单个的全局唯一 id
     * 当前方法会抛出 com.daikun.common.exception.BizException
     * @return 唯一 id
     */
    public static long nextSingletonId() {
        // noinspection HttpUrlsUsage
        String defaultUrl = (isTestEnv ? "http://test-vencryption.daikuns.com:8090" : "https://tha-encrypt.daikuns.com") + singletonUrl;
        // 首先从 nacos 获取配置的地址，如果获取不到，使用默认接口地址
        String url = StrUtil.blankToDefault(SpringUtil.getProperty("sys.param.idGen.singletonUrl"), defaultUrl);

        try (HttpResponse resp = HttpUtil.createGet(url + "?sysId=" + sysId).timeout(timeout).execute()) {
            if (resp.getStatus() != HttpStatus.HTTP_OK) {
                log.info("IdGenUtil.nextSingletonId 获取单个唯一 id 失败, 请求失败 -> {}", resp.getStatus());
                throw new BizException(BaseResultEnum.SERVICE_CALL_FAIL);
            }
            String body = resp.body();
            if (!JSONUtil.isTypeJSONObject(body)) {
                log.info("IdGenUtil.nextSingletonId 获取单个唯一 id 失败, 响应结果无法解析 -> {}", body);
                throw new BizException(BaseResultEnum.SERVICE_CALL_FAIL);
            }
            // 这个对象中包含的字段有 code message data，code 200 成功
            JSONObject obj = JSONUtil.parseObj(body);
            if (obj.getInt("code") != 200) {
                log.info("IdGenUtil.nextSingletonId 获取单个唯一 id 失败 -> resp = {}", obj);
                throw new BizException(BaseResultEnum.SERVICE_CALL_FAIL);
            }
            Integer data = obj.getInt("data");
            if (data == null) {
                log.info("IdGenUtil.nextSingletonId 获取单个唯一 id 失败，返回值错误 -> resp = {}", obj);
                throw new BizException(BaseResultEnum.SERVICE_CALL_FAIL);
            }
            return data;
        } catch (Exception e) {
            log.info("IdGenUtil.nextSingletonId 获取单个唯一 id 失败，msg = {}", e.getMessage(), e);
            return -1;
        }
    }

    /**
     * 批量获取全局唯一 id
     * @param num 获取唯一 id 个数
     * @return num 个数的唯一 id，返回 空集合 时，接口异常，批量获取失败
     */
    @SuppressWarnings("unchecked")
    public static List<Long> nextBatchIds(int num) {
        int batchSize = Math.min(Math.max(num, 1), Integer.parseInt(StrUtil.blankToDefault(SpringUtil.getProperty("sys.param.idGen.batchSize"), "1000")));

        // noinspection HttpUrlsUsage
        String defaultUrl = (isTestEnv ? "http://test-vencryption.daikuns.com:8090" : "https://tha-encrypt.daikuns.com") + batchUrl;
        // 首先从 nacos 获取配置的地址，如果获取不到，使用默认接口地址
        String url = StrUtil.blankToDefault(SpringUtil.getProperty("sys.param.idGen.batchUrl"), defaultUrl);

        try (HttpResponse resp = HttpUtil.createGet(url + "?batchSize=" + batchSize + "&sysId=" + sysId).timeout(timeout).execute()) {
            if (resp.getStatus() != HttpStatus.HTTP_OK) {
                log.info("IdGenUtil.nextBatchIds 批量获取唯一 id 失败, 请求失败 -> {}", resp.getStatus());
                throw new BizException(BaseResultEnum.SERVICE_CALL_FAIL);
            }
            String body = resp.body();
            if (!JSONUtil.isTypeJSONObject(body)) {
                log.info("IdGenUtil.nextBatchIds 批量获取唯一 id 失败, 响应结果无法解析 -> {}", body);
                throw new BizException(BaseResultEnum.SERVICE_CALL_FAIL);
            }
            JSONObject obj = JSONUtil.parseObj(body);
            if (obj.getInt("code") != 200) {
                log.info("IdGenUtil.nextBatchIds 批量获取唯一 id 失败，返回值错误 -> dr = {}", obj);
                throw new BizException(BaseResultEnum.SERVICE_CALL_FAIL);
            }
            if (obj.get("data") == null || JSONUtil.isTypeJSONArray(obj.get("data").toString())) {
                log.info("IdGenUtil.nextBatchIds 批量获取唯一 id 失败，返回值错误 -> dr = {}", obj);
                throw new BizException(BaseResultEnum.SERVICE_CALL_FAIL);
            }
            JSONArray data = obj.getJSONArray("data");
            List<Long> ids = new ArrayList<>();
            for (Object datum : data) {
                if (datum != null && NumberUtil.isNumber(datum.toString())) {
                    ids.add(Long.parseLong(datum.toString()));
                }
            }
            if (ids.size() != batchSize) {
                log.info("IdGenUtil.nextBatchIds 批量获取唯一 id 失败，获取结果与参数不匹配 -> num = {}, dr = {}", batchSize, obj);
                throw new BizException(BaseResultEnum.SERVICE_CALL_FAIL);
            }
            return ids;
        } catch (Exception e) {
            log.info("IdGenUtil.nextBatchIds 批量获取唯一 id 失败，msg = {}", e.getMessage(), e);
            return ListUtil.empty();
        }
    }

}
